<!-- no doctype - it makes IE ignore the height: 100%. -->
<html>
<head>
<title>toolbarFrame</title>
<style type="text/css">
html,body { width: 100%; height: 100%; border: none; margin: 0; padding: 0; overflow: hidden; }
button { height: 100%; }
</style>

<script _src="xpathinput.js">
function initXPather() {
  xpath = new XPathInput( document.getElementById('xpath'),
                          parent.opener.document );
  var width = (innerWidth - getViewOffset(xpath.node, false).x - 2);
  xpath.node.style.width = width +"px";
}

(function(_) {

const highlightCSS = "chrome://firebug/content/highlighter.css";
const xhtmlNS = "http://www.w3.org/1999/xhtml";

// ************************************************ firebug/lib.js:

function move(element, x, y) {
  element.style.left = x + "px";
  element.style.top = y + "px";
}

function resize(element, w, h) {
  element.style.width = w + "px";
  element.style.height = h + "px";
}

function hasClass(node, name) {
  if (!node || node.nodeType != 1)
    return false;
  else {
    var re = new RegExp("(^|\\s)"+name+"($|\\s)");
    return re.exec(node.getAttribute("class")) != null;
  }
}

function setClass(node, name) {
  if (node && !hasClass(node, name))
    node.className += " " + name;
}

function removeClass(node, name) {
  if (node && node.className) {
    var index = node.className.indexOf(name);
    if (index >= 0) {
      var size = name.length;
      node.className =
        node.className.substr(0, index-1) +
        node.className.substr(index+size);
    }
  }
}

function getBody(doc) {
  return doc.body || doc.getElementsByTagName("body")[0];
}

function getViewOffset(elt, singleFrame) {
  function addOffset(elt, coords, view) {
    var p = elt.offsetParent;
    coords.x += elt.offsetLeft - (p ? p.scrollLeft : 0);
    coords.y += elt.offsetTop - (p ? p.scrollTop : 0);

    if (p) {
      if (p.nodeType == 1) {
        var parentStyle = view.getComputedStyle(p, "");
        if (parentStyle.position != "static") {
          coords.x += parseInt(parentStyle.borderLeftWidth);
          coords.y += parseInt(parentStyle.borderTopWidth);

          if (p.localName == "TABLE") {
            coords.x += parseInt(parentStyle.paddingLeft);
            coords.y += parseInt(parentStyle.paddingTop);
          }
          else if (p.localName == "BODY") {
            var style = view.getComputedStyle(elt, "");
            coords.x += parseInt(style.marginLeft);
            coords.y += parseInt(style.marginTop);
          }
        }
        else if (p.localName == "BODY") {
          coords.x += parseInt(parentStyle.borderLeftWidth);
          coords.y += parseInt(parentStyle.borderTopWidth);
        }

        var parent = elt.parentNode;
        while (p != parent) {
          coords.x -= parent.scrollLeft;
          coords.y -= parent.scrollTop;
          parent = parent.parentNode;
        }
        addOffset(p, coords, view);
      }
    }
    else {
      if (elt.localName == "BODY") {
        var style = view.getComputedStyle(elt, "");
        coords.x += parseInt(style.borderLeftWidth);
        coords.y += parseInt(style.borderTopWidth);

        var htmlStyle = view.getComputedStyle(elt.parentNode, "");
        coords.x -= parseInt(htmlStyle.paddingLeft);
        coords.y -= parseInt(htmlStyle.paddingTop);
      }

      if (elt.scrollLeft)
        coords.x += elt.scrollLeft;
      if (elt.scrollTop)
        coords.y += elt.scrollTop;

      var win = elt.ownerDocument.defaultView;
      if (win && (!singleFrame && win.frameElement))
        addOffset(win.frameElement, coords, win);
    }
  }

  var coords = {x: 0, y: 0};
  if (elt)
    addOffset(elt, coords, elt.ownerDocument.defaultView);

  return coords;
}

function readBoxStyles(style) {
  const styleNames = {
    "margin-top": "marginTop", "margin-right": "marginRight",
    "margin-left": "marginLeft", "margin-bottom": "marginBottom",
    "border-top-width": "borderTop", "border-right-width": "borderRight",
    "border-left-width": "borderLeft", "border-bottom-width": "borderBottom",
    "padding-top": "paddingTop", "padding-right": "paddingRight",
    "padding-left": "paddingLeft", "padding-bottom": "paddingBottom"
  };

  var styles = {};
  for (var styleName in styleNames)
    if (styleNames.hasOwnProperty(styleName))
      styles[styleNames[styleName]] = parseInt(
        style.getPropertyCSSValue(styleName).cssText
      );
  return styles;
}

function addStyleSheet(doc, link) {
  var heads = doc.getElementsByTagName("head");
  if (heads.length)
    heads[0].appendChild(link);
  else
    doc.documentElement.appendChild(link);
}

function createStyleSheet(doc, url) {
  var link = doc.createElementNS(xhtmlNS, "link");
  link.setAttribute("charset", "utf-8");
  link.firebugIgnore = true;
  link.setAttribute("rel", "stylesheet");
  link.setAttribute("type", "text/css");
  link.setAttribute("href", url);
  return link;
}

// ************************************************ firebug/inspector.js:

function pad(element, t, r, b, l) {
  element.style.padding = Math.abs(t) + "px " + Math.abs(r) + "px "
    + Math.abs(b) + "px " + Math.abs(l) + "px";
}

function FrameHighlighter(edgeSize) {
  this.edgeSize = edgeSize || 2;
}

FrameHighlighter.prototype = {
  setEdgeSize: function(n) {
    return this.edgeSize = n;
  },

  highlight: function(context, element) {
    var offset = getViewOffset(element, true);
    var x = offset.x, y = offset.y;
    var w = element.offsetWidth, h = element.offsetHeight;

    var nodes = this.getNodes(context, element);

    var edgeSize = this.edgeSize;
    move(nodes.top, x, y-edgeSize);
    resize(nodes.top, w, edgeSize);

    move(nodes.right, x+w, y-edgeSize);
    resize(nodes.right, edgeSize, h+edgeSize*2);

    move(nodes.bottom, x, y+h);
    resize(nodes.bottom, w, edgeSize);

    move(nodes.left, x-edgeSize, y-edgeSize);
    resize(nodes.left, edgeSize, h+edgeSize*2);

    var body = getNonFrameBody(element);
    if (!body)
      return this.unhighlight(context);

    var needsAppend = !nodes.top.parentNode ||
                       nodes.top.ownerDocument != body.ownerDocument;
    if (needsAppend) {
      attachStyles(context, body);
      for (var edge in nodes) if (nodes.hasOwnProperty(edge)) {
        try {
          body.appendChild(nodes[edge]);
        } catch(exc) {}
      }
    }
  },

  unhighlight: function(context) {
    var nodes = this.getNodes(context);
    var body = nodes.top.parentNode;
    if (body) {
      for (var edge in nodes)
        if (nodes.hasOwnProperty(edge))
          body.removeChild(nodes[edge]);
    }
  },

  getNodes: function(context) {
    if (!context.frameHighlighter) {
      var doc = context.window.document;

      function createEdge(name) {
        var div = doc.createElementNS(xhtmlNS, "div");
        div.firebugIgnore = true;
        div.className = "firebugHighlight";
        return div;
      }

      context.frameHighlighter = {
        top: createEdge("Top"),
        right: createEdge("Right"),
        bottom: createEdge("Bottom"),
        left: createEdge("Left")
      };
    }

    return context.frameHighlighter;
  }
};

// ************************************************

function BoxModelHighlighter(showRulers) {
  this.showRulers = showRulers;
}

BoxModelHighlighter.prototype = {
  highlight: function(context, element, boxFrame) {
    var nodes = this.getNodes(context);
    var highlightFrame = boxFrame ? nodes[boxFrame] : null;

    if (context.highlightFrame)
      removeClass(context.highlightFrame, "firebugHighlightBox");

    context.highlightFrame = highlightFrame;

    if (highlightFrame) {
      setClass(nodes.offset, "firebugHighlightGroup");
      setClass(highlightFrame, "firebugHighlightBox");
    }
    else
      removeClass(nodes.offset, "firebugHighlightGroup");

    var win = element.ownerDocument.defaultView;
    if (!win)
      return;

    var offsetParent = element.offsetParent;
    if (!offsetParent)
      return;

    var parentStyle = win.getComputedStyle(offsetParent, "");
    var parentOffset = getViewOffset(offsetParent, true);
    var parentX = parentOffset.x + parseInt(parentStyle.borderLeftWidth);
    var parentY = parentOffset.y + parseInt(parentStyle.borderTopWidth);
    var parentW = offsetParent.offsetWidth-1;
    var parentH = offsetParent.offsetHeight-1;

    var style = win.getComputedStyle(element, "");
    var styles = readBoxStyles(style);

    var offset = getViewOffset(element, true);
    var x = offset.x - Math.abs(styles.marginLeft);
    var y = offset.y - Math.abs(styles.marginTop);
    var w = element.offsetWidth - (styles.paddingLeft + styles.paddingRight
        + styles.borderLeft + styles.borderRight);
    var h = element.offsetHeight - (styles.paddingTop + styles.paddingBottom
        + styles.borderTop + styles.borderBottom);

    move(nodes.offset, x, y);
    pad(nodes.margin, styles.marginTop, styles.marginRight,
        styles.marginBottom, styles.marginLeft);
    pad(nodes.border, styles.borderTop, styles.borderRight,
        styles.borderBottom, styles.borderLeft);
    pad(nodes.padding, styles.paddingTop, styles.paddingRight,
        styles.paddingBottom, styles.paddingLeft);
    resize(nodes.content, w, h);

    var showLines = this.showRulers && boxFrame;
    if (showLines) {
      move(nodes.parent, parentX, parentY);
      resize(nodes.parent, parentW, parentH);

      if (parentX < 14)
        setClass(nodes.parent, "overflowRulerX");
      else
        removeClass(nodes.parent, "overflowRulerX");

      if (parentY < 14)
        setClass(nodes.parent, "overflowRulerY");
      else
        removeClass(nodes.parent, "overflowRulerY");

      var left = x;
      var top = y;
      var width = w-1;
      var height = h-1;

      if (boxFrame == "content") {
        left += Math.abs(styles.marginLeft) + Math.abs(styles.borderLeft)
          + Math.abs(styles.paddingLeft);
        top += Math.abs(styles.marginTop) + Math.abs(styles.borderTop)
          + Math.abs(styles.paddingTop);
      }
      else if (boxFrame == "padding") {
        left += Math.abs(styles.marginLeft) + Math.abs(styles.borderLeft);
        top += Math.abs(styles.marginTop) + Math.abs(styles.borderTop);
        width += Math.abs(styles.paddingLeft) + Math.abs(styles.paddingRight);
        height += Math.abs(styles.paddingTop) + Math.abs(styles.paddingBottom);
      }
      else if (boxFrame == "border") {
        left += Math.abs(styles.marginLeft);
        top += Math.abs(styles.marginTop);
        width += Math.abs(styles.paddingLeft) + Math.abs(styles.paddingRight)
           + Math.abs(styles.borderLeft) + Math.abs(styles.borderRight);
        height += Math.abs(styles.paddingTop) + Math.abs(styles.paddingBottom)
          + Math.abs(styles.borderTop) + Math.abs(styles.borderBottom);
      }
      else if (boxFrame == "margin") {
        width += Math.abs(styles.paddingLeft) + Math.abs(styles.paddingRight)
           + Math.abs(styles.borderLeft) + Math.abs(styles.borderRight)
           + Math.abs(styles.marginLeft) + Math.abs(styles.marginRight);
        height += Math.abs(styles.paddingTop) + Math.abs(styles.paddingBottom)
          + Math.abs(styles.borderTop) + Math.abs(styles.borderBottom)
          + Math.abs(styles.marginTop) + Math.abs(styles.marginBottom);
      }

      move(nodes.lines.top, 0, top);
      move(nodes.lines.right, left+width, 0);
      move(nodes.lines.bottom, 0, top+height);
      move(nodes.lines.left, left, 0)
    }

    var body = getNonFrameBody(element);
    if (!body)
      return this.unhighlight(context);

    var needsAppend = !nodes.offset.parentNode
      || nodes.offset.parentNode.ownerDocument != body.ownerDocument;

    if (needsAppend) {
      attachStyles(context, body);
      body.appendChild(nodes.offset);
    }

    var lines = nodes.lines;
    if (showLines) {
      if (!lines.top.parentNode) {
        body.appendChild(nodes.parent);

        for (var line in lines)
          if (lines.hasOwnProperty(line))
            body.appendChild(lines[line]);
      }
    }
    else if (lines.top.parentNode) {
      body.removeChild(nodes.parent);

      for (var line in lines)
        if (lines.hasOwnProperty(line))
          body.removeChild(lines[line]);
    }
  },

  unhighlight: function(context) {
    var nodes = this.getNodes(context);
    if (nodes.offset.parentNode) {
      var body = nodes.offset.parentNode;
      body.removeChild(nodes.offset);

      var lines = nodes.lines;
      if (lines.top.parentNode) {
        body.removeChild(nodes.parent);

        for (var line in lines)
          body.removeChild(lines[line]);
      }
    }
  },

  getNodes: function(context) {
    if (!context.boxModelHighlighter) {
      var doc = context.window.document;

      function createRuler(name) {
        var div = doc.createElementNS(xhtmlNS, "div");
        div.firebugIgnore = true;
        div.className = "firebugRuler firebugRuler"+name;
        return div;
      }

      function createBox(name) {
        var div = doc.createElementNS(xhtmlNS, "div");
        div.firebugIgnore = true;
        div.className = "firebugLayoutBox firebugLayoutBox"+name;
        return div;
      }

      function createLine(name) {
        var div = doc.createElementNS(xhtmlNS, "div");
        div.firebugIgnore = true;
        div.className = "firebugLayoutLine firebugLayoutLine"+name;
        return div;
      }

      var nodes = context.boxModelHighlighter = {
        parent: createBox("Parent"),
        rulerH: createRuler("H"),
        rulerV: createRuler("V"),
        offset: createBox("Offset"),
        margin: createBox("Margin"),
        border: createBox("Border"),
        padding: createBox("Padding"),
        content: createBox("Content"),
        lines: {
          top: createLine("Top"),
          right: createLine("Right"),
          bottom: createLine("Bottom"),
          left: createLine("Left")
        }
      };

      nodes.parent.appendChild(nodes.rulerH);
      nodes.parent.appendChild(nodes.rulerV);
      nodes.offset.appendChild(nodes.margin);
      nodes.margin.appendChild(nodes.border);
      nodes.border.appendChild(nodes.padding);
      nodes.padding.appendChild(nodes.content);
    }

    return context.boxModelHighlighter;
  }
};

function getNonFrameBody(elt) {
  var body = getBody(elt.ownerDocument);
  return body.localName.toUpperCase() == "FRAMESET" ? null : body;
}

function attachStyles(context, body) {
  var doc = body.ownerDocument;
  if (!context.highlightStyle)
    context.highlightStyle = createStyleSheet(doc, highlightCSS);

  if (!context.highlightStyle.parentNode ||
       context.highlightStyle.ownerDocument != doc)
    addStyleSheet(body.ownerDocument, context.highlightStyle);
}

_.getViewOffset = getViewOffset;
_.FrameHighlighter = FrameHighlighter;
_.BoxModelHighlighter = BoxModelHighlighter;

})(this);



// uses:
//   http://ecmanaut.googlecode.com/svn/trunk/lib/bind.js
//   http://ecmanaut.googlecode.com/svn/trunk/lib/firebug/highlighter.js

function bind(fn, self) {
  var args = [].slice.call(arguments, 2);
  return function() {
    fn.apply(self, args.concat([].slice.call(arguments)));
  };
}

// (function(_) {

function throttle(fn, self, delay) {
  function fire() {
    delete throttle[fn];
    self[fn].apply(self, args);
  }
  window.console && console.count && console.count(fn);
  if (throttle[fn]) return;
  var args = [].slice.call(arguments, 3);
  throttle[fn] = setTimeout(fire, delay||50);
}

function scrollToNode(node) {
  var x = node, y = arguments[1];
  if (typeof node == "object") {
    node = getViewOffset(node, true);
    x = node.x;
    y = node.y;
  } else if (arguments.length == 1) {
    y = x;
    x = 0;
  }
  window.scrollTo(x, y);
}

function XPathInput(node, doc) {
  this.dirty = false;
  this.focused = 0;
  this.node = node;
  this.doc = doc || document;
  this.init(node);
}

XPathInput.prototype = {
  init: function(node) {
    function listen(node, event) {
      //console.info("%x.listen(%x, %x)", this, node, event);
      var fn = bind(this["on"+event], this);
      node.addEventListener(event, fn, true);
      this.undo[event] = function() {
        node.removeEventListener(event, fn, true);
      }
    }

    //console.log("%x.init(%x) - %x", this, node, this.node == node);
    if (this.node != node)
      this.undo();
    this.node = node;

    ["blur", "focus", //"mouseover", "mouseout",
     "keyup", "DOMMouseScroll"].forEach(bind(listen, this, node));
    //listen.call(this, this.doc, "DOMNodeInserted");

    //this.test(true);
  },

  undo: function undo() {
    for (var event in undo)
      if (undo.hasOwnProperty(event) && "function" == typeof undo[event])
        undo[event]();
  },


  onDOMNodeInserted: function() { this.dirty = true; },

  onblur:      function() { throttle("unhighlight", this); },
  onmouseout:  function() { throttle("unhighlight", this); },

  onfocus:     function() { throttle("test", this); },
  onmouseover: function() { throttle("test", this); },

  onkeyup: function(event) {
    //console.log("onkeyup(%x)", event);
    var key = { 38: "up", 40: "down" }[event.keyCode];
    if (key)
      this["on"+key]();
    else
      throttle("test", this, 100);
  },

  onDOMMouseScroll: function(event) {
    //console.log("onDOMMouseScroll(%x)", event);
    var delta = 0;
    if (event.wheelDelta) { // Opera
      delta = event.wheelDelta/120;
    } else if (event.detail) {
      delta = event.detail/3; // In Mozilla, delta is a multiple of 3
    }
    if (delta < 0) this.onup();
    if (delta > 0) this.ondown();
    event.stopPropagation();
    event.preventDefault();
  },

  onup:   function() { this.focus(-1); },
  ondown: function() { this.focus(1); },
  focus: function(n) {
    //console.log("focus(%x)", n);
    var count = (this.contexts || []).length;
    var focused = this.focused;
    if (count) {
      while (focused < 0)
        focused += count;
      this.focused = (focused + (n||0) + count) % count;
      this.unfocus();
      var context = this.contexts[focused];
      if (context.node.nodeType == 1) {
        context.fill = new BoxModelHighlighter(false);
        context.fill.highlight(context, context.node);
      }
    }
  },

  unfocus: function() {
    function undo(context) {
      if (context.fill) {
        context.fill.unhighlight(context);
        delete context.fill;
      }
    }
    this.contexts.forEach(undo);
  },

  highlight: function(nodes) {
    function go(node) {
      var context = { window:win, node:node };
      if (node.nodeType == 1) {
        context.outline = new FrameHighlighter(1);
        context.outline.highlight(context, node);
      }
      return context;
    }
    //defaultStatus = ("highlight() %x" + arguments.callee.caller);
    if (this.contexts)
      this.unhighlight();
    console.time("Highlight "+ nodes.length);
    var win = this.doc.defaultView;
    this.contexts = nodes.map(go);
    this.focus();
    console.timeEnd("Highlight "+ nodes.length);
  },

  unhighlight: function() {
    function undo(context) {
      if (context.outline)
        context.outline.unhighlight(context);
      if (context.fill)
        context.fill.unhighlight(context);
    }
    //defaultStatus = ("unhighlight() %x"+arguments.callee.caller);
    var n = (this.contexts || []).length
    console.time("Unhighlight "+ n);
    (this.contexts || []).forEach(undo);
    delete this.contexts;
    this.dirty = true;
    console.timeEnd("Unhighlight "+ n);
  },

  test: function(nohilight) {
    var xpath = this.node.value, doc = this.doc, col = "", title = "";
    if (xpath == this.lastXPath && !this.dirty) return undefined;
    //console.log("test(%x) %x", nohilight, arguments.callee.caller);
    try {
      var got = doc.evaluate(xpath, doc, null, 0, null), result = [], next;
      switch (got.resultType) {
        case got.STRING_TYPE: result = got.stringValue; col = "#CC0"; break;
        case got.NUMBER_TYPE: result = got.numberValue; col = "#00C"; break;
        case got.BOOLEAN_TYPE:result = got.booleanValue;col = "#C0C"; break;
        default: col = "#0C0";
          while (next = got.iterateNext())
            result.push(next);
      }
      if ("object" == typeof result) {
        title = "NodeList: "+ result.length +" item"+ (result.length==1?"":"s");
        if (!result.length) col = "";
        var eq = function(a, b) {
          if (!a || !b || a.length != b.length) return false;
          for (var i = 0; i < a.length; i++)
            if (a[i] !== b[i]) return false;
          return true;
        }
        if (!eq(result, this.lastResult)) {
          this.lastResult = result;
          nohilight || this.highlight(result);
        }
      } else {
        title = typeof result +": "+ result;
        nohilight || this.unhighlight();
      }
    } catch(e) {
      nohilight || this.unhighlight();
      result = undefined;
      title = e.message;
      col = "#F00";
    }
    this.node.title = title;
    this.node.style.borderColor = col;
    this.lastXPath = xpath;
    this.dirty = nohilight || false;
    //console.warn(title);
    window.result = result;
    parent.document.title = title;
    return result;
  }
};

//})(this);

</script>

<script type="text/javascript">

var outputFrame = parent.outputFrame;
var inputFrame = parent.inputFrame;
var framesetElement = parent.document.documentElement.getElementsByTagName("frameset")[0];

var savedRows;


// Need to use C-style comments in handleError() and print() 
// because IE retains them when decompiling a function.



function print(s, c) {
  var outputFrame = parent.outputFrame; /* duplicated here in case this function is elsewhere */
  var doc = outputFrame.document;

  var newdiv = doc.createElement("div");
  newdiv.appendChild(doc.createTextNode(s));
  if (c)
    newdiv.style.color = c;
  doc.body.appendChild(newdiv);
}

function handleError(er, file, lineNumber) 
{
  print("Error on line " + lineNumber + ": " + er, "red"); 
    
  /* Find the character offset for the line */
  /* (code adapted from blogidate xml well-formedness bookmarklet) */
  var ta = inputFrame.document.getElementById("input");
  var lines = ta.value.split("\n");
  var cc = 0; 
  var i;
  for(i=0; i < (lineNumber - 1); ++i) 
    cc += lines[i].length + 1;

  /* Hacky(?) workaround for IE's habit of including \r's in strings */
  if (ta.value.split("\r").length > 1)
    cc -= lineNumber - 1;

  /* Select the line */
  if(document.selection) { 
    /* IE (Leonard Lin gave me this code) */
    var sel = ta.createTextRange(); 
    sel.moveStart("character", cc); 
    sel.collapse(); 
    sel.moveEnd("character", lines[i].length); 
    sel.select();
  } else { 
    /* Mozilla */
    ta.selectionStart = cc; 
    ta.selectionEnd = cc + lines[i].length; 
  }
 
  /* return true; */ /* nah, let the error go through to IE's js consolish thing! */
}





function showHideOutput()
{
  if (outputFrame.document.body.clientHeight > 100) {
    // hide
    savedRows = framesetElement.rows;    
    framesetElement.rows = "25,*,0";
  }
  else {
    // show. use the previous size, if possible
    if (savedRows) {
      framesetElement.rows = savedRows;
      savedRows = null;
    }
    else {
      framesetElement.rows = "25,*,*";
    }
  }
}

function refocus()
{
  inputFrame.document.getElementById("input").focus();
}


function clearOutput()
{
  var b = outputFrame.document.body;
  while(b.firstChild)
    b.removeChild(b.firstChild);
}

function stripLineBreaks(s)
{
  return s.replace(/\n/g, "").replace(/\r/g, ""); // stripping \r is for IE
}

function execute()
{
  var js = inputFrame.document.getElementById("input").value;

  var useOpener = parent.opener && !parent.opener.closed;
  var oldStyle = !! document.all; // lame but meh.
  
  print("Running" + (useOpener ? " in bookmarklet mode" : "") + (oldStyle ? " in make-IE-happy mode" : "") + "...", "orange");

  if (useOpener)
    executeWithJSURL(js, parent.opener); // only way to execute against another frame
  else if (oldStyle)
    executeWithDW(js, execFrame); // only way to get line numbers in IE
  else
    executeWithJSURL(js, execFrame); // faster in Mozilla  
}

// Advantages: can get line numbers in IE.
function executeWithDW(js, win)
{
  win.document.open();
  win.inputFrame = inputFrame;
  win.outputFrame = outputFrame;
  win.document.write(
    stripLineBreaks(
        '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">' +
        '<html><head><title>execFrame<\/title><script type="text/javascript">'
      + print         // yay for decompilation!
      + handleError 
      + "window.onerror = handleError;"
      + "<\/script><\/head>"
      ) 
    + '<body><script type="text/javascript">'
    + js         // should escape it a little to remove the string <\/script> at least...
    + "<\/script><\/body><\/html>"
    );
  win.document.close();
}

// Advantages: can be used to inject a script into another window, faster in Mozilla.
function executeWithJSURL(js, win)
{
  // isolate scope
  js = "(function(){ " + js + " \n })()";

  win.print = print;
  win.onerror = handleError;

  // double encodeURIComponent because javascript: URLs themselves are encoded!
  win.location.href = 'javascript:eval(decodeURIComponent("' + encodeURIComponent(encodeURIComponent(js)) + '")); void 0;';
  
  refocus();
}

// Other ideas I haven't tried lately: create a <script> element, eval.


function makeUserScript(userScriptLink)
{
    userScriptLink.href = 
        "data:text/javascript;charset=utf-8," + 
        encodeURIComponent(inputFrame.document.getElementById("input").value + "//.user.js");
}

</script>

</head>

<body onload="initXPather()">

<button accesskey="E" onclick="execute(); refocus();"><u>E</u>xecute</button>
<!-- <button accesskey="R" onclick="reloadAndExecute(); refocus();"><u>R</u>eload and execute</button> -->
<button accesskey="C" onclick="clearOutput(); refocus();"><u>C</u>lear output</button>
<button accesskey="S" onclick="showHideOutput(); refocus();"><u>S</u>how/hide output</button>
<!-- <button accesskey="H" onclick="help(); refocus();"><u>H</u>elp</button> -->

<a href="data:text/html,..." onfocus="makeUserScript(this);" onmouseover="makeUserScript(this);" target="_blank">Install as user script</a>

<input type="text" id="xpath" />

<div style="visibility: hidden">
<iframe name="execFrame" width="5" height="5" src="about:blank"></iframe>
</div>

</body>
</html>
